#include <iostream>
#include <vector>
#include <string>
#include <boost/filesystem.hpp>

#include "util.hpp"

const std::string src_path = "data/input/";
const std::string output = "data/raw_html/raw.txt";

typedef struct DocInfo
{
    std::string title;
    std::string desc;
    std::string url;
} DocInfo;

// 函数声明
bool EnumFile(std::vector<std::string> *files_list, const std::string &src_path);

bool ParseHtml(std::vector<DocInfo> *results, const std::vector<std::string> &files_list);

bool SaveHtml(const std::vector<DocInfo> &results, const std::string &output);

int main()
{
    // 1、将每一个HTML文件的路径和文件名保存起来，方便使用
    std::vector<std::string> files_list;
    if (!EnumFile(&files_list, src_path))
    {
        std::cerr << "EnumFile error" << std::endl;
        return 1;
    }

    // 2、将每一个文件去标签、清洗后保存成一个结构体，存放在vector中
    std::vector<DocInfo> results;
    if (!ParseHtml(&results, files_list))
    {
        std::cerr << "ParseHtml error" << std::endl;
        return 2;
    }

    // 3、将每一个结构体存放在raw.txt中
    if (!SaveHtml(results, output))
    {
        std::cerr << "SaveHtml error" << std::endl;
        return 3;
    }

    return 0;
}

bool EnumFile(std::vector<std::string> *files_list, const std::string &src_path)
{
    namespace fs = boost::filesystem;
    fs::path root_path(src_path);
    // 如果路径不存在就不再往后操作了
    if (!fs::exists(root_path))
    {
        std::cerr << "src_path is not exist" << std::endl;
        return false;
    }

    // 定义一个空迭代器，用来判断递归结束
    fs::recursive_directory_iterator end;
    for (fs::recursive_directory_iterator iter(root_path); iter != end; iter++)
    {
        // 如果该文件不是普通文件就跳过
        if (!fs::is_regular_file(*iter))
            continue;
        // 如果该文件不是以.html为后缀就跳过
        if (iter->path().extension() != ".html")
            continue;

        // 走到这里就是正确的文件了，将文件路径和文件名保存起来
        // std::cout << iter->path().string() << std::endl;
        files_list->push_back(iter->path().string());
    }
    return true;
}

static bool ParseTitle(std::string &out, std::string *title)
{
    // 找到title的部分 <title>这个地方就是我们想要的数据</title>
    size_t pos1 = out.find("<title>");
    if (pos1 == std::string::npos)
        return false;
    size_t pos2 = out.find("</title>");
    if (pos2 == std::string::npos)
        return false;

    pos1 += std::string("<title>").size();
    if (pos1 > pos2)
        return false;
    *title = out.substr(pos1, pos2 - pos1);
    return true;
}

static bool ParseContent(std::string &out, std::string *content)
{
    // 去标签，只要内容
    enum Status
    {
        LABLE,
        CONTENT
    };

    int s = LABLE;
    for (auto c : out)
    {
        switch (s)
        {
        case LABLE:
            if (c == '>')
                s = CONTENT;
            break;
        case CONTENT:
            if (c == '<')
                s = LABLE;
            else
            {
                if (c == '\n')
                    c = ' ';
                content->push_back(c);
            }
            break;

        default:
            break;
        }
    }
    return true;
}

static bool ParseUrl(const std::string &path_name, std::string *url)
{
    // 拼接url
    // https://www.boost.org/doc/libs/1_84_0/
    std::string url_head = "https://www.boost.org/doc/libs/1_84_0/doc/html";

    // std::string url_head = "https://www.boost.org/doc/libs/1_78_0/doc/html";
    std::string url_tail = path_name.substr(src_path.size() - 1);
    *url = url_head + url_tail;
    return true;
}

static void ShowDoc(DocInfo doc)
{
    std::cout << doc.title << std::endl;
    std::cout << doc.desc << std::endl;
    std::cout << doc.url << std::endl;
}

bool ParseHtml(std::vector<DocInfo> *results, const std::vector<std::string> &files_list)
{
    namespace bs = boost::filesystem;
    for (auto &path_name : files_list)
    {
        // 打开该文件，将文件内容读取到
        std::string out;
        if (!Util::FileUtil::Read(path_name, &out))
            continue;

        DocInfo doc;
        // 提取出title
        if (!ParseTitle(out, &(doc.title)))
            continue;

        // 提取出content
        if (!ParseContent(out, &(doc.desc)))
            continue;

        // 提取出URL
        if (!ParseUrl(path_name, &(doc.url)))
            continue;

        // 将DocInfo对象push到results中
        results->push_back(std::move(doc));
        // ShowDoc(doc);
        // break;
    }
    return true;
}

bool SaveHtml(const std::vector<DocInfo> &results, const std::string &output)
{
    std::ofstream outf(output, std::ios::out | std::ios::binary);
    if (!outf.is_open())
    {
        std::cerr << "open" << output << "error" << std::endl;
        return false;
    }

    // 写入文件，文档内字段用\3分割，文档之间用\n分割
    for (auto &item : results)
    {
        std::string out_string;
        out_string += item.title;
        out_string += "\3";
        out_string += item.desc;
        out_string += "\3";
        out_string += item.url;
        out_string += "\n";

        outf.write(out_string.c_str(), out_string.size());
    }

    outf.close();
    return true;
}